/**
 * 
 */
package com.rangers.auth.base.service;

import java.io.Serializable;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import com.rangers.auth.base.db.UserDbAccess;
import com.rangers.auth.base.details.AuthUser;
import com.rangers.auth.base.details.BriefUser;
import com.rangers.auth.base.exception.UsernameExistsException;

/**
 * 用户业务
 * 
 * @version 1.0
 * @author fuhw
 * @date 2017年8月17日 下午6:07:21
 */
@SuppressWarnings("rawtypes")
@Service
public class UserServiceHandler<T extends BriefUser, AU extends AuthUser, ACCESS extends UserDbAccess<T, ID>, ID extends Serializable>
		implements UserService<T, AU, ACCESS, ID> {

	private final ACCESS access;
	@SuppressWarnings("unchecked")
	private final AU auth = (AU) new AuthUser();

	@Autowired
	private PasswordEncoder passwordEncoder;

	@Autowired
	public UserServiceHandler(ACCESS access) {
		super();
		this.access = access;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.security.core.userdetails.UserDetailsService#
	 * loadUserByUsername(java.lang.String)
	 */
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		T user = this.findByUsername(username);
		if (user == null) {
			throw new UsernameNotFoundException(username + "not found");
		}
		Map<String, Collection<? extends GrantedAuthority>> authContexts = new LinkedHashMap<>();
		BeanUtils.copyProperties(user, auth);
		auth.setAuthContexts(authContexts);
		return auth;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.rangers.auth.base.service.UserService#get(java.io.Serializable)
	 */
	@Override
	public T get(ID id) {
		Assert.notNull(id, "the token of object must be not null");
		return access.findOne(id);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.rangers.auth.base.service.UserService#list()
	 */
	@Override
	public List<T> list() {
		return (List<T>) access.findAll();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.rangers.auth.base.service.UserService#create(com.rangers.auth.base.
	 * entity.BriefUser)
	 */
	@Override
	@Transactional
	public T create(T entity) {
		argumentValidate(true, entity);
		return access.save(entity);
	}

	private void argumentValidate(boolean isAdd, T entity) {
		Assert.notNull(entity, "the entity for create must be not null");
		Assert.isTrue(StringUtils.hasText(entity.getUsername()), "the username must be not null");
		String password = entity.getPassword();
		Assert.isTrue(StringUtils.hasText(password), "the password must be not null");
		if (access.existsByUsername(entity.getUsername())) {
			throw new UsernameExistsException("the username has been exists");
		}
		entity.setPassword(passwordEncoder.encode(password));
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.rangers.auth.base.service.UserService#update(com.rangers.auth.base.
	 * entity.BriefUser)
	 */
	@Override
	@Transactional
	public T update(T entity) {
		argumentValidate(false, entity);
		return access.save(entity);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.rangers.auth.base.service.UserService#delete(java.io.Serializable)
	 */
	@Override
	public void delete(ID id) {
		access.delete(id);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.rangers.auth.base.service.UserService#findByUsername(java.lang.
	 * String)
	 */
	@Override
	public T findByUsername(String username) {
		Assert.hasText(username, "the username must be not null");
		return access.findByUsername(username);
	}

}
